/*
	Билинейная интерполяция
*/

#include <assert.h>
#include <stdlib.h>
#include "binpol.h"
#include "linpol.h"

/* Создание объекта полинома */
binpol_t *binpol_new(const int binpol_degr_x, const int binpol_degr_y)
{
	assert(binpol_degr_x > 0);
	assert(binpol_degr_y > 0);

	/* Выделение памяти под объект полинома */
	binpol_t *binpol_o = malloc(BINPOL_T_SZ);
	if(!binpol_o) return 0;

	/* Выделение памяти под данные X и Y */
	void *mem_n = 0;

	mem_n = linpol_alloc_mem(binpol_degr_x + 1);
	if(!mem_n) return 0;

	binpol_o->vals_x = mem_n;

	mem_n = linpol_alloc_mem(binpol_degr_y + 1);
	if(!mem_n) return 0;

	binpol_o->vals_y = mem_n;

	/* Выделение памяти под матрицу значений Z */
	double **mat_n = calloc(binpol_degr_x + 1, sizeof(double *));
	if(!mat_n) return 0;

	for(int i = 0; i <= binpol_degr_x; i++)
	{
		*(mat_n + i) = linpol_alloc_mem(binpol_degr_y + 1);
		if(!*(mat_n + i)) return 0;
	}

	binpol_o->vals_z = mat_n;

	/* Установка параметров полинома */
	binpol_o->binpol_degr_x = binpol_degr_x;
	binpol_o->binpol_degr_y = binpol_degr_y;

	return binpol_o;
}

/* Удаление объекта полинома */
void binpol_destroy(binpol_t *binpol_o)
{
	assert(binpol_o);

	/* Освобождение памяти под значения X и Y */
	free(binpol_o->vals_x);
	free(binpol_o->vals_y);

	/* Освобождение памяти под значения Z */
	int pnts_xc = binpol_o->binpol_degr_x + 1;
	double **mat_n = binpol_o->vals_z;

	for(int i = 0; i < pnts_xc; i++)
	{
		free(*(mat_n + i));
	}

	free(mat_n);

	/* Освобождение памяти под объект полинома */
	free(binpol_o);
}

/* Генерирование исходных данных */
void binpol_gen_data(const binpol_t *binpol_o, point_t args_start, point_t args_end, double (*gen_func) (const point_t args))
{
	assert(binpol_o);
	assert(gen_func);

	/* Количество исходных точек */
	int pnts_xc = binpol_o->binpol_degr_x + 1;
	int pnts_yc = binpol_o->binpol_degr_y + 1;

	/* Интервал значений */
	double arg_x_step = (args_end.arg_x - args_start.arg_x) / (double) (pnts_xc - 1);

	double arg_y_step = (args_end.arg_y - args_start.arg_y) / (double) (pnts_yc - 1);

	/* Генерирование данных X */
	for(int i = 0; i < pnts_xc; i++)
	{
		binpol_o->vals_x[i] = args_start.arg_x;
		args_start.arg_x += arg_x_step;
	}

	/* Генерирование данных Y */
	for(int i = 0; i < pnts_yc; i++)
	{
		binpol_o->vals_y[i] = args_start.arg_y;
		args_start.arg_y += arg_y_step;
	}

	/* Генерирование данных Z */
	for(int i = 0; i < pnts_xc; i++)
	{
		for(int j = 0; j < pnts_yc; j++)
		{
			point_t args = {binpol_o->vals_x[i], binpol_o->vals_y[j]};
			binpol_o->vals_z[i][j] = gen_func(args);
		}
	}
}

/* Вычисление значения полинома */
double binpol_get_val(binpol_t *binpol_o, const point_t args)
{
	assert(binpol_o);

	/* Количество исходных точек */
	int pnts_c = binpol_o->binpol_degr_x + 1;

	/* Создание линейного полинома */
	linpol_t *linpol_o = malloc(LINPOL_T_SZ);
	assert(linpol_o);

	/* Интерполирование по строкам */
	linpol_o->vals_x = binpol_o->vals_y;
	linpol_o->linpol_degr = binpol_o->binpol_degr_y;

	/* Выделение памяти под точки интерполирования */
	double *mem_t = linpol_alloc_mem(pnts_c);
	assert(mem_t);

	/* Интерполирование */
	for(int i = 0; i < pnts_c; i++)
	{
		linpol_o->vals_y = binpol_o->vals_z[i];
		mem_t[i] = linpol_get_val(linpol_o, args.arg_y);
	}

	/* Интерполирование по столбцу */
	linpol_o->vals_x = binpol_o->vals_x;
	linpol_o->vals_y = mem_t;
	linpol_o->linpol_degr = binpol_o->binpol_degr_x;

	/* Вычисление конечного значения */
	double val_r = linpol_get_val(linpol_o, args.arg_x);

	/* Освобождение памяти под полином */
	free(mem_t);
	free(linpol_o);

	return val_r;
}